#include "content/data_provider_impl.hpp"
#include <any>
#include <cmath>
#include <memory>
#include "content/bindable.hpp"
#include "content/data_provider.hpp"
#include "content/image.hpp"
#include "content/view_base.hpp"
#include "core/assert.hpp"
#include "core/log.hpp"
#include "utils/safe_value.hpp"
#include "utils/string_utils.hpp"
#include "content/data_cast.hpp"

bool parse_bool (const std::string &data)
{
    auto lower = to_lower (data);
    trim (lower);
    if (data == "true")
    {
        return true;
    }
    else if (data == "false")
    {
        return false;
    }
    else
    {
        double d   = 0.0;
        char * end = nullptr;
        strtod (lower.c_str (), &end);
        if (end != nullptr)
        {
            return std::fabs (d) > 0;
        }
        return false;
    }
}

class dyn_data : public data_provider
{
public:
    dyn_data (std::string bind_key, data_flags compatible):
        flags (compatible), category (none), key (bind_key), store_type(value_type::NONE)
    {
        data_ptr = (packed*)store;
        memset(data_ptr, 0, sizeof(*data_ptr));
    }

    data_category type () const override
    {
        PI_ASSERT (category != none);
        return category;
    }

    void attach (bindable_base *data) override
    {
        auto ret = data->get_data (key);
        if (ret.has_value ())
        {
            auto [t,v] = *ret;
            store_type = t;
            switch (t)
            {
            case value_type::BOOL: {
                if (flags & (boolean))
                {
                    category = (boolean);
                    data_ptr->pb = std::any_cast<bool*> (v);
                }
                break;
            }
            case value_type::U8: {
                if (flags & (unsigned_integral))
                {
                    category = (unsigned_integral);
                    data_ptr->pu8 = std::any_cast<u8*> (v);
                }
                break;
            }
            case value_type::U16: {
                if (flags & (unsigned_integral))
                {
                    category = (unsigned_integral);
                    data_ptr->pu16 = std::any_cast<u16*> (v);
                }
                break;
            }
            case value_type::U32: {
                if (flags & (unsigned_integral))
                {
                    category = (unsigned_integral);
                    data_ptr->pu32 = std::any_cast<u32*> (v);
                }
                break;
            }
            case value_type::U64: {
                if (flags & (unsigned_integral))
                {
                    category = (unsigned_integral);
                    data_ptr->pu64 = std::any_cast<u64*> (v);
                }
                break;
            }
            case value_type::S8: {
                if (flags & (signed_integral))
                {
                    category = (signed_integral);
                    data_ptr->ps8 = std::any_cast<s8*> (v);
                }
                break;
            }
            case value_type::S16: {
                if (flags & (signed_integral))
                {
                    category = (signed_integral);
                    data_ptr->ps16 = std::any_cast<s16*> (v);
                }
                break;
            }
            case value_type::S32: {
                if (flags & (signed_integral))
                {
                    category = (signed_integral);
                    data_ptr->ps32 = std::any_cast<s32*> (v);
                }
                break;
            }
            case value_type::S64: {
                if (flags & (signed_integral))
                {
                    category = (signed_integral);
                    data_ptr->ps64 = std::any_cast<s64*> (v);
                }
                break;
            }
            case value_type::F32: {
                if (flags & (floating_point))
                {
                    category = (floating_point);
                    data_ptr->pf32 = std::any_cast<f32*> (v);
                }
                break;
            }
            case value_type::F64: {
                if (flags & (floating_point))
                {
                    category = (floating_point);
                    data_ptr->pf64 = std::any_cast<f64*> (v);
                }
                break;
            }
            // TODO: Support
            // case value_type::VEC2:
            // case value_type::VEC3:
            // case value_type::VEC4:
            case value_type::STD_STRING: {
                if (flags & (std_string))
                {
                    category = (std_string);
                    data_ptr->str = std::any_cast<std::string *> (v);
                }
                break;
            }
            case value_type::C_STRING: {
                if (flags & c_string) {
                    data_ptr->cstr = std::any_cast<c_string_t>(v);
                }
                break;
            }
            case value_type::CALLBACK: {
                if (flags & callback) {
                    data_ptr->callback = std::any_cast<callback_t>(v);
                }
                break;
            }
            case value_type::ENUMERATE: {
                if (flags & enumerate) {
                    data_ptr->select = std::any_cast<enumerate_t>(v);
                }
                break;
            }
            case value_type::IMAGE: {
                if (flags & (image))
                {
                    category = (image);
                    data_ptr->img = std::any_cast<ImageContent **> (v);
                }
                break;
            }
            case value_type::UI: {
                if (flags & (ui))
                {
                    category = (ui);
                    data_ptr->ui = std::any_cast<ui_view **> (v);
                    if (*data_ptr->ui)
                        (*data_ptr->ui)->bind(data);
                }
                break;
            }
            default: PI_ASSERT_MSG (0, "Unsupported!!");
            }
        }
        else
        {
            L_ERROR ("data '%s' was not found!", key.c_str());
        }
    }

    s64 get_s64 () override
    {
        s64 v;
        constexpr auto to_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == to_type) {
            v = *((packed*)store)->ps64;
        } else if (can_cast(store_type, to_type)) {
            do_cast(((packed*)store)->common, store_type, &v, to_type);
        } else {
            v = data_provider::get_s64();
        }
        return v;
    }

    void set_s64 (s64 v) override
    {
        constexpr auto from_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == from_type) {
            *((packed*)store)->ps64 = v;
        } else if (can_cast(from_type, store_type)) {
            do_cast(&v, from_type, ((packed*)store)->common, store_type);
        } else {
            data_provider::set_s64(v);
        }
    }

    u64 get_u64 () override
    {
        u64 v;
        constexpr auto to_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == to_type) {
            v = *((packed*)store)->pu64;
        } else if (can_cast(store_type, to_type)) {
            do_cast(((packed*)store)->common, store_type, &v, to_type);
        } else {
            v = data_provider::get_u64();
        }
        return v;
    }

    void set_u64 (u64 v) override
    {
        constexpr auto from_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == from_type) {
            *((packed*)store)->pu64 = v;
        } else if (can_cast(from_type, store_type)) {
            do_cast(&v, from_type, ((packed*)store)->common, store_type);
        } else {
            data_provider::set_u64(v);
        }
    }

    f64 get_f64 () override
    {
        f64 v;
        constexpr auto to_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == to_type) {
            v = *((packed*)store)->pf64;
        } else if (can_cast(store_type, to_type)) {
            do_cast(((packed*)store)->common, store_type, &v, to_type);
        } else {
            v = data_provider::get_f64();
        }
        return v;
    }

    void set_f64 (f64 v) override
    {
        constexpr auto from_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == from_type) {
            *((packed*)store)->pf64 = v;
        } else if (can_cast(from_type, store_type)) {
            do_cast(&v, from_type, ((packed*)store)->common, store_type);
        } else {
            data_provider::set_f64(v);
        }
    }

    bool get_boolean () override
    {
        bool v;
        constexpr auto to_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == to_type) {
            v = *((packed*)store)->pb;
        } else if (can_cast(store_type, to_type)) {
            do_cast(((packed*)store)->common, store_type, &v, to_type);
        } else {
            v = data_provider::get_boolean();
        }
        return v;
    }

    void set_boolean (bool v) override
    {
        constexpr auto from_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == from_type) {
            *((packed*)store)->pb = v;
        } else if (can_cast(from_type, store_type)) {
            do_cast(&v, from_type, ((packed*)store)->common, store_type);
        } else {
            data_provider::set_boolean(v);
        }
    }

    std::string get_std_string () override
    {
        std::string v;
        constexpr auto to_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == to_type) {
            v = *((packed*)store)->str;
        } else if (can_cast(store_type, to_type)) {
            do_cast(((packed*)store)->common, store_type, &v, to_type);
        } else {
            v = data_provider::get_std_string();
        }
        return v;
    }

    void set_std_string (std::string v) override
    {
        constexpr auto from_type = (value_type)value_traits<decltype(v)>::store_type;
        if (store_type == from_type) {
            *((packed*)store)->str = v;
        } else if (can_cast(from_type, store_type)) {
            do_cast(&v, from_type, ((packed*)store)->common, store_type);
        } else {
            data_provider::set_std_string(v);
        }
    }

    c_string_t get_cstring () override
    {
        c_string_t v{};
        if (store_type == value_type::C_STRING) {
            v = ((packed*)store)->cstr;
        } else {
            v = data_provider::get_cstring();
        }
        return v;
    }

    callback_t get_callback () override
    {
        callback_t v{};
        if (store_type == value_type::CALLBACK) {
            v = ((packed*)store)->callback;
        } else {
            v = data_provider::get_callback();
        }
        return v;
    }

    enumerate_t get_enumerate () override
    {
        enumerate_t v{};
        if (store_type == value_type::ENUMERATE) {
            v = ((packed*)store)->select;
        } else {
            v = data_provider::get_enumerate();
        }
        return v;
    }

    ImageContent *get_image () override
    {
        ImageContent *ic{};
        if (store_type == value_type::IMAGE) {
            ic = *((packed*)store)->img;
        } else {
            ic = data_provider::get_image();
        }
        return ic;
    }

    ui_view *get_ui () override
    {
        ui_view *v{};
        if (store_type == value_type::UI) {
            v = *((packed*)store)->ui;
        } else {
            v = data_provider::get_ui();
        }
        return v;
    }

private:
    data_flags const  flags;
    data_category     category;
    std::string const key;

    union packed
    {
        union
        {
            void*          common;
 
            s8*            ps8;
            s16*           ps16;
            s32*           ps32;
            s64*           ps64;
            u8*            pu8;
            u16*           pu16;
            u32*           pu32;
            u64*           pu64;
            f32*           pf32;
            f64*           pf64;
            bool*          pb;
            ImageContent** img;
            ui_view**      ui;
            std::string*   str;
        };

        callback_t  callback;
        enumerate_t select;
        c_string_t  cstr;
    };

    value_type store_type;
    char store[sizeof (packed)]{};
    packed *data_ptr{};
};

static data_provider_p create_dynamic_data (const std::string &info, data_flags compatible)
{
    return std::make_shared<dyn_data> (info, compatible);
}

static data_provider_p create_static_data (const std::string &info, data_flags compatible)
{
    if (compatible & signed_integral)
    {
        return std::make_shared<s64_data_provider> (info);
    }
    else if (compatible & unsigned_integral)
    {
        return std::make_shared<u64_data_provider> (info);
    }
    else if (compatible & floating_point)
    {
        return std::make_shared<f64_data_provider> (info);
    }
    else if (compatible & boolean)
    {
        return std::make_shared<boolean_data_provider> (info);
    }
    else if (compatible & std_string)
    {
        return std::make_shared<std_string_data_provider> (info);
    }
    else
    {
        // not support
        return {};
    }
}

data_provider_p create_data (const std::string &info, data_flags compatible)
{
    if (info.starts_with ("${") && info.ends_with ("}"))
    {
        return create_dynamic_data (info.substr (2, info.length () - 3), compatible);
    }
    if (!info.empty ())
    {
        return create_static_data (info, compatible);
    }
    return {};
}
